#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/nl80211.h>

void analysis_rtmsg(struct rtmsg *rtmsg, int len)
{
	struct rtattr *rta = RTM_RTA(rtmsg);
	for (struct rtattr *attr = rta; RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
		//printf("rta->type = %u\n", attr->rta_type);
		struct in_addr *addr;
		switch (attr->rta_type) {
		case RTA_DST:
			addr = RTA_DATA(attr);
			char host[1024];
			inet_ntop(AF_INET, addr, host, sizeof(host));
			printf("DST ADDR=%s\n", host);
			break;
		case RTA_SRC:
			addr = RTA_DATA(attr);
			inet_ntop(AF_INET, addr, host, sizeof(host));
			printf("DST ADDR=%s\n", host);
			break;
		case RTA_PREFSRC:
			addr = RTA_DATA(attr);
			inet_ntop(AF_INET, addr, host, sizeof(host));
			printf("PREFSRC ADDR=%s\n", host);
			break;
		case RTA_GATEWAY:
			addr = RTA_DATA(attr);
			inet_ntop(AF_INET, addr, host, sizeof(host));
			printf("GATEWAY ADDR=%s\n", host);
			break;
		}
	}
	printf("\n********************\n");
}

void analysis(const struct nlmsghdr *nhm, int len)
{
	printf("msg_len=%d recv len=%d\n", nhm->nlmsg_len, len);
	struct nlmsghdr *nh = NULL;
	for (nh = (void *)nhm; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
		switch (nh->nlmsg_type) {
		case NLMSG_DONE:
			printf("recv NLMSG_DONE\n");
			break;
		case NLMSG_ERROR:
			printf("recv NLMSG_ERROR\n");
			break;
		case NLMSG_NOOP:
			printf("recv NLMSG_NOOP\n");
			break;
		case RTM_NEWADDR:
			printf("recv RTM_NEWADDR\n");
			break;
		case RTM_DELADDR:
			printf("recv RTM_DELADDR\n");
			break;
		case RTM_GETADDR:
			printf("recv RTM_GETADDR\n");
			break;
		case RTM_GETROUTE:
		case RTM_NEWROUTE:
			printf("recv RTM_GETROUTE\n");
			struct rtmsg *rtmsg = NLMSG_DATA(nh);
			if (rtmsg->rtm_type == RTN_UNICAST) {
				analysis_rtmsg(rtmsg, nh->nlmsg_len - NLMSG_SPACE(sizeof(*rtmsg)));
			}

			break;
		default:
			printf("unknow msg type %u\n", nh->nlmsg_type);
			break;
		}
	}
}

int main()
{
	int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
	if (fd < 0) {
		perror("socket");
		exit(0);
	}
	struct sockaddr_nl sa;
	memset(&sa, 0, sizeof(sa));
	sa.nl_family = AF_NETLINK;
	sa.nl_pid = getpid();
	sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;
	if (bind(fd, (void *)&sa, sizeof(sa)) < 0) {
		perror("bind");
		exit(0);
	}

	struct sockaddr_nl dest_addr;
	memset(&dest_addr, 0, sizeof(dest_addr));
	dest_addr.nl_family = AF_NETLINK;
	dest_addr.nl_pid = 0;
	dest_addr.nl_groups = 0;

	struct {
		struct nlmsghdr nh;
		struct rtmsg rtmsg;
		char attrbuf[512];
	} req;
	struct rtattr *rta;

	memset(&req, 0, sizeof(req));
	req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
	req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
	req.nh.nlmsg_type = RTM_GETROUTE;
	req.nh.nlmsg_seq = 1;
	req.rtmsg.rtm_family = AF_INET;
	req.rtmsg.rtm_dst_len = 0;
	req.rtmsg.rtm_src_len = 0;

	// rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.nh.nlmsg_len));
	// rta->rta_type = RTA_GATEWAY;
	// rta->rta_len = RTA_LENGTH(sizeof(struct sockaddr_in));
	// req.nh.nlmsg_len = NLMSG_ALIGN(req.nh.nlmsg_len) + RTA_LENGTH(sizeof(struct sockaddr_in));

	int rc = send(fd, &req, req.nh.nlmsg_len, 0);
	if (rc < 0) {
		perror("send()");
		exit(EXIT_FAILURE);
	}
	printf("send(): %d\n", rc);

	char buf[8192];
	struct nlmsghdr *nh = (void *)buf;
	int len = 0;
	while ((len = recv(fd, buf, sizeof(buf), 0)) > 0) {
		analysis(nh, len);
	}

	return 0;
}
